package cn.abcsys.cloud.devops.web.ldap;
 
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;

import javax.naming.AuthenticationException;
import javax.naming.Context;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException; 
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.Control;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;

import cn.abcsys.cloud.devops.web.entity.User;


/**
 * 匿名登录LDAP
 * @author bona
 *
 */
@Component
public class LdapAnonImpl implements LdapAnon{
	private Logger logger = Logger.getLogger(this.getClass());
	ConfigureCommon cfg = new ConfigureCommon("config/ldap.properties");
	
	private String LDAPURL = cfg.getKey("ldap.url");  
	private String AUTHENTICATION=cfg.getKey("ldap.authentication");
    private String BASEDN = cfg.getKey("ldap.base_dn"); //"cn=Manager"; 非匿名登陆时需要配置的basedn
    private String FACTORY = cfg.getKey("ldap.factory");
   // private String CREDENTIALS=cfg.getKey("ldap.credentials");
	private String LDAPAccount = cfg.getKey("ldap.user_dn");  //"cn=Manager"; 非匿名登陆时需要配置的basedn
	private String LDAPPassword = cfg.getKey("ldap.credentials"); //"123456"; 非匿名登陆需要配置的密码
    private String LDAPAnonymouse = cfg.getKey("ldap.anonymous"); //true 匿名登陆   false 非匿名登陆 
    private String LDAPEnable = cfg.getKey("ldap.enable"); //true 使用ldap,false不使用ldap
    private LdapContext ldapCtx = null;  
    private Hashtable<String, String> env = null;  
    private Control[] connCtls = null;  
    
    
    /**
	 * 功能：链接Ldap
	 * 编码：huyn
	 * 
	 */
	public int connect() {
		logger.info("connecting...");
		env = new Hashtable<String, String>();
		env.put(Context.INITIAL_CONTEXT_FACTORY, FACTORY);
		env.put(Context.PROVIDER_URL, LDAPURL + LDAPAccount);// LDAP server
		env.put(Context.SECURITY_AUTHENTICATION, AUTHENTICATION);
		// 此处若不指定用户名和密码,则自动转换为匿名登录
	    if ("false".equalsIgnoreCase(LDAPAnonymouse)) {
			env.put(Context.SECURITY_PRINCIPAL, BASEDN+"," + LDAPAccount);
			env.put(Context.SECURITY_CREDENTIALS, LDAPPassword);
		}

		try {
			ldapCtx = new InitialLdapContext(env, connCtls);
			logger.info("connected.");
			return 0;
		} catch (javax.naming.AuthenticationException e) {
			logger.info("Authentication faild: "+e.toString());  
			return -1;
		} catch (Exception e) {
			logger.info("Something wrong while authenticating: "+e.toString());  
			return -1;
		}
	}

	 
    public String getUserDN(String uid){  
        String userDN = "";  
        try{  
			SearchControls constraints = new SearchControls();  
			constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);
			NamingEnumeration<SearchResult> entries = ldapCtx.search("", "uid="+uid, constraints); 
			if(entries == null){  
				logger.info("Have no NamingEnumeration.");  
			}  
			if(!entries.hasMoreElements()){  
				logger.info("Have no element.");  
			}  
			while (entries != null && entries.hasMoreElements()){//maybe more than one element  
			    Object obj = entries.nextElement();  
			    if(obj instanceof SearchResult){  
			        SearchResult si = (SearchResult) obj;  
			        userDN += si.getName();  
			        userDN += "," + LDAPAccount;  
			    }else{  
				   logger.info(obj);  
			    }  
			}  
          }catch(Exception e){  
        	  logger.info("Exception in search():"+e);  
          }  
        return userDN;  
    }  
     
     
    /**
     * 根据进行用户uid和用户密码进行ldap认证
     * 1 认证通过  0 认作未通过,需要进行数据库登陆认证   -1 不允许登陆
     */
    public int authenricate(String uid,String password){  
		if("false".equalsIgnoreCase(LDAPEnable)){
			return 0;
		}
        int valide = -1;  
        String userDN = "";
        if(this.connect() < 0) {
        	return 0;
        }
 
        try {          	
            userDN = getUserDN(uid);  
     		if(null == userDN || "".equals(userDN)) {
     			this.close();
     			return -1;
     		}
     		
        	ldapCtx.addToEnvironment(Context.SECURITY_PRINCIPAL,userDN);  
        	ldapCtx.addToEnvironment(Context.SECURITY_CREDENTIALS,password);  
        	ldapCtx.reconnect(connCtls);  
            logger.info(userDN + " is authenticated");              
            valide = 1;  
        }catch (AuthenticationException e) {  
        	logger.info(userDN + " is not authenticated");  
        	logger.info(e.toString());  
            valide = -1;  
        }catch (NamingException e) {  
        	logger.info(userDN + " is not authenticated");  
            valide = -1;  
        }  
        finally{
			this.close();
		}
        return valide;  
    }  
    public List<User> searchAll() throws NamingException {
		
		logger.info("ldap searching all...");
		SearchControls searchCtls = new SearchControls();
		searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
		String searchFilter = cfg.getKey("ldap.filter");
		//String searchBase=cfg.getKey("ldap.base_dn");
		String returnedAtts[] = { "uid","mail","cn","entryDN"};
		searchCtls.setReturningAttributes(returnedAtts);
		NamingEnumeration<SearchResult> entries = ldapCtx.search("", searchFilter, searchCtls);

		List<User> userList = new ArrayList<User>();
		while (entries.hasMoreElements()) {
			SearchResult entry = entries.next();
			//logger.info(">>>" + entry.getName());
			// Print out the groups
			Attributes attrs = entry.getAttributes();
			if (attrs != null) {
				User user = new User();
				for (NamingEnumeration<? extends Attribute> names = attrs.getAll(); names.hasMore();) {
					Attribute attr = names.next();
					String key = attr.getID();
					Enumeration values = attr.getAll();
					while ( values.hasMoreElements()) {
						if (key.equals("uid")) {
							user.setLdapUid(values.nextElement().toString());						
						} else if (key.equals("mail")) {
							user.setUserMail(values.nextElement().toString());
						}else if (key.equals("cn")) {
							user.setUserName(values.nextElement().toString());
						}else {
							values.nextElement();
						}
					}
				}
				if(null!=user.getLdapUid()){
					System.out.println(user);
					userList.add(user);
				}
			}
		}
		logger.info("Search all complete.");
		return userList;
	} 
    public void close(){
		logger.info("closing...");
		try {
			if(ldapCtx!=null){
				ldapCtx.close();
			}
		} catch (NamingException e) {
			e.printStackTrace();
		}
		ldapCtx=null;
		logger.info("closed.");
	} 
}
    
